/*
 * Copyright 2017 the original author or authors.
 *
 */

package com.ivo.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <h1>反射工具类</h1>
 * <p>
 * content
 * </p>
 * <b>Note:</b>
 *
 * @author nexf
 * @since 2015/10/2 13:46
 */
public class ReflectionUtil {

  public static final String CGLIB_CLASS_SEPARATOR = "$$";
  private static final Logger logger = LoggerFactory.getLogger(ExceptionUtil.class);

  /**
   * 调用Getter方法.
   */
  public static Object invokeGetter(Object obj, String propertyName) {
    String getterMethodName = "get" + StringUtil.capitalize(propertyName);
    return invokeMethod(obj, getterMethodName, new Object[]{}, new Class[]{});
  }

  /**
   * 调用Setter方法.使用value的Class来查找Setter方法.
   */
  public static void invokeSetter(Object obj, String propertyName, Object value) {
    invokeSetter(obj, propertyName, value, null);
  }

  /**
   * 调用Setter方法.
   *
   * @param propertyType 用于查找Setter方法,为空时使用value的Class替代.
   */
  public static void invokeSetter(Object obj, String propertyName, Object value,
      Class<?> propertyType) {
    Class<?> type = propertyType != null ? propertyType : value.getClass();
    String setterMethodName = "set" + StringUtil.capitalize(propertyName);
    invokeMethod(obj, setterMethodName, new Object[]{value}, new Class[]{type});
  }

  /**
   * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
   */
  public static Object getFieldValue(final Object obj, final String fieldName) {
    try {
      return FieldUtils.readField(obj, fieldName, true);
    } catch (IllegalAccessException e) {
      throw ExceptionUtil.unchecked(e);
    }
  }

  /**
   * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
   */
  public static Object getFieldValue(final Object obj, final Field field) {
    try {
      return FieldUtils.readField(field, obj, true);
    } catch (IllegalAccessException e) {
      throw ExceptionUtil.unchecked(e);
    }
  }

  /**
   * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
   */
  public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
    try {
      FieldUtils.writeField(obj, fieldName, value, true);
    } catch (IllegalAccessException e) {
      throw ExceptionUtil.unchecked(e);
    }
  }

  /**
   * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
   */
  public static void setFieldValue(final Object obj, final Field field, final Object value) {
    try {
      FieldUtils.writeField(field, obj, value, true);
    } catch (IllegalAccessException e) {
      throw ExceptionUtil.unchecked(e);
    }
  }

  /**
   * 对于被cglib AOP过的对象, 取得真实的Class类型.
   */
  public static Class<?> getActualClass(Class<?> clazz) {
    if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
      Class<?> superClass = clazz.getSuperclass();
      if (superClass != null && !Object.class.equals(superClass)) {
        return superClass;
      }
    }
    return clazz;
  }

  /**
   * 直接调用对象方法, 无视private/protected修饰符. 用于一次性调用的情况.
   */
  public static Object invokeMethod(final Object obj, final String methodName, final Object[] args,
      final Class<?>[] parameterTypes) {
    try {
      return MethodUtils.invokeMethod(obj, true, methodName, args, parameterTypes);
    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
      throw ExceptionUtil.unchecked(e);
    }
  }

  /**
   * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. 如无法找到, 返回Object.class.
   *
   * @param clazz The class to introspect
   * @return the first generic declaration, or Object.class if cannot be determined
   */
  public static Class getSuperClassGenericType(final Class clazz) {
    return getSuperClassGenericType(clazz, 0);
  }

  /**
   * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. 如无法找到, 返回Object.class.
   * <p/>
   * 如public UserDao extends HibernateDao<User,Long>
   *
   * @param clazz clazz The class to introspect
   * @param index the Index of the generic declaration,start from 0.
   * @return the index generic declaration, or Object.class if cannot be determined
   */
  public static Class getSuperClassGenericType(final Class clazz, final int index) {
    Type genType = clazz.getGenericSuperclass();

    // 让泛型基类可以支持二层继承
    if (!(genType instanceof ParameterizedType)) {
      Class<?> superClazz = clazz.getSuperclass();
      genType = superClazz.getGenericSuperclass();
    }

    if (!(genType instanceof ParameterizedType)) {
      logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
      return Object.class;
    }

    Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
    if (index >= params.length || index < 0) {
      logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName()
          + "'s Parameterized Type: " + params.length);
      return Object.class;
    }
    if (!(params[index] instanceof Class)) {
      logger.warn(
          clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
      return Object.class;
    }
    return (Class) params[index];
  }

  public static Map<String, Object> toMap(Object obj) {
    Map<String, Object> map = new HashMap<>();
    List<Field> fields = FieldUtils.getAllFieldsList(obj.getClass());
    for (Field field : fields) {
      String name = field.getName();
      if (name.equals("serialVersionUID")) {
        continue;
      }
      map.put(name, getFieldValue(obj, field));
    }
    return map;
  }


}
